// vue API
import { createApp } from "vue";
// element-plus API
import ElementPlus from "element-plus";

// ol API
import Control from "ol/control/Control";
import {
  Group as LayerGroup,
  Image as ImageLayer,
  Vector as VectorLayer,
  Tile as TileLayer,
  Graticule as GraticuleLayer,
  Heatmap as HeatmapLayer,
  VectorTile as VectorTileLayer,
  WebGLTile as WebGLTileLayer,
  WebGLVector as WebGLVectorLayer,
  Layer,
} from "ol/layer";

import FlowLayer from "ol/layer/Flow";
import {
  Vector as VectorSource,
  ImageStatic,
  WMTS as WMTSource,
} from "ol/source";
import BaseLayer from "ol/layer/Base";
import Collection from "ol/Collection";
import { listen, unlistenByKey } from "ol/events";

/**
 * 执行栈节点，用于遍历图层
 * @typedef {Object} StackNode
 * @property {Collection<BaseLayer>|LayerGroup} source - 来源(map中的图层)，图层集合或图层组
 * @property {LayerNode|Array<LayerNode>} target - 目标(树形结构中的图层节点)，图层节点或图层节点数组
 */

/**
 * 生成随机id
 * @returns {string} 随机id
 */
function getRandomId() {
  const chars =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  const length = 6; // 生成32位随机字符串
  let result = "";
  const array = new Uint8Array(length);
  window.crypto.getRandomValues(array);
  for (let i = 0; i < length; i++) {
    result += chars.charAt(array[i] % chars.length);
  }
  return result;
}

/**
 * 获取图层类型 (后续会补充更多图层类型)
 * @param {BaseLayer} layer - OpenLayers 图层对象
 * @returns {string} 图层类型标识
 * @description 根据图层实例类型返回对应的类型标识
 */
function getLayerType(layer) {
  if (layer instanceof VectorLayer) {
    // 矢量图层
    return "vector";
  } else if (layer instanceof TileLayer) {
    // 瓦片图层
    return "tile";
  } else if (layer instanceof ImageLayer) {
    // 图片图层
    return "image";
  } else if (layer instanceof LayerGroup) {
    // 图层组
    return "group";
  } else if (layer instanceof GraticuleLayer) {
    // 经纬网图层
    return "graticule";
  } else if (layer instanceof HeatmapLayer) {
    // 热力图层
    return "heatmap";
  } else if (layer instanceof VectorTileLayer) {
    // 矢量瓦片图层
    return "vectorTile";
  } else if (layer instanceof WebGLTileLayer) {
    // WebGL瓦片图层
    return "webGLTile";
  } else if (layer instanceof WebGLVectorLayer) {
    // WebGL矢量图层
    return "webGLVector";
  } else if (layer instanceof Layer) {
    // 基础图层
    return "layer";
  } else if (layer instanceof FlowLayer) {
    // 流场图层
    return "flow";
  }
  // 未知图层类型
  return "unknown";
}

function getLayerById(map, targetId) {
  const layers = [...map.getLayers().getArray(), ...map.getAllLayers()];
  let targetLayer;
  for (let i = 0; i < layers.length; i++) {
    const layer = layers[i];
    if (!layer || !(layer instanceof BaseLayer)) continue;
    const id = layer.get("id") || layer.id;
    if (id === targetId) {
      targetLayer = layer;
      break;
    }
  }
  return targetLayer;
}

/**
 * 图层节点类
 * @description 表示图层管理器中的单个图层节点，用于构建图层树形结构
 * @class LayerNode
 */
class LayerNode {
  /**
   * 创建图层节点实例
   * @param {BaseLayer|Object} layer - OpenLayers 图层对象
   * @param {LayerNode|Object|Array} parentNode - 父节点或父节点数组
   * @description 根据 OpenLayers 图层对象创建图层节点，并建立父子关系
   */
  constructor(layer, parentNode) {
    const isAutoSet = layer instanceof BaseLayer;

    /**
     * 图层唯一标识符
     * @type {string}
     * @description 从图层对象中获取或生成随机ID
     */
    this.id = layer.id;

    if (isAutoSet && !this.id) {
      this.id = layer.get("id") || getRandomId();
      layer.id = this.id;
    }

    /**
     * 图层显示名称
     * @type {string}
     * @description 从图层对象中获取名称，如果不存在则使用ID作为名称
     */
    this.name = isAutoSet
      ? layer.name || layer.get("name")
      : layer.name || this.id;
    if (!this.name) {
      this.name = this.id;
      layer.name = this.name;
    }

    /**
     * 图层可见性状态
     * @type {boolean}
     * @description 控制图层是否在地图上显示
     */
    this.visible = isAutoSet ? layer.get("visible") : layer.visible ?? true;

    /**
     * 图层透明度值
     * @type {number}
     * @description 取值范围 0-1，0 表示完全透明，1 表示完全不透明
     */
    this.opacity = isAutoSet ? layer.get("opacity") : layer.opacity ?? 1;

    /**
     * 图层层级索引
     * @type {number}
     * @description 控制图层的绘制顺序，值越大越靠上层
     */
    this.zIndex = (isAutoSet ? layer.get("zIndex") : layer.zIndex) ?? 0;

    /**
     * 是否为图层组
     * @type {boolean}
     * @description 标识该节点是否包含子图层
     */
    this.group = false;

    if (isAutoSet) {
      if (
        layer instanceof LayerGroup &&
        layer.getLayers().getArray().length > 0
      ) {
        this.group = layer.group ?? layer.get("group") ?? true;
      }
    } else {
      if (layer.children && layer.children.length > 0) {
        this.group = true;
      }
    }

    /**
     * 子图层节点数组
     * @type {Array<LayerNode>}
     * @description 当节点为图层组时，存储其子图层节点
     */
    if (this.group) {
      this.children = [];
    }

    /**
     * 图层类型标识
     * @type {string}
     * @description 图层的类型标识，如 'vector'、'tile'、'image' 等
     */
    this.type = isAutoSet
      ? layer.type || layer.get("type") || getLayerType(layer)
      : layer.type || "unknown";

    /**
     * 父图层节点ID
     * @type {string|null}
     * @description 父节点的唯一标识符，根节点为 null
     */
    this.pid = null;

    // 建立父子关系
    if (parentNode instanceof LayerNode) {
      parentNode.children.unshift(this);
      this.pid = parentNode.id;
    } else {
      parentNode.unshift(this);
    }
  }
}

/**
 * @typedef {Object} Options
 * @property {string} className - 控件类名
 * @property {HTMLElement|string} target - 控件目标
 * @property {Function} render - 渲染函数
 * @property {Array<LayerNode>|null} treeData - 图层树形结构
 */

class LayersManager extends Control {
  /**
   * @param {Options} options 图层管理器选项
   */
  constructor(options = {}) {
    const element = document.createElement("div");
    element.className = options.className || "ol-custom-layer-tree";
    super({
      element: element,
      target: options.target,
      render: options.render,
    });

    /**
     * 控件元素
     * @type {HTMLDivElement}
     */
    this.element_ = element;

    /**
     * 图层列表
     * @type {Array<LayerNode>}
     */
    this.layerList = [];
    /**
     * 图层树形结构
     * @type {Array<LayerNode>}
     */
    this.layerTree = [];

    /**
     * 事件监听器数组
     * @type {Array}
     */
    this.listenKeys = [];

    /**
     * 是否自动更新图层树形结构
     * @type {boolean}
     */
    this.isAutoUpdate = true;

    /**
     * 图层树形结构
     * @type {Array<LayerNode>}
     */
    this.treeData_ = null;

    this.setTreeData(options.treeData);
  }

  /**
   * @override
   */
  setMap(map) {
    super.setMap(map);
    if (map) {
      this.handleMapChange();
      // 侦听地图图层数量变化
      this.listenKeys.push(
        listen(
          map.getLayers(),
          "change:length",
          this.handleMapChange.bind(this)
        ),
        listen(
          map.getLayerGroup(),
          "change:layers",
          this.handleMapChange.bind(this)
        )
      );
    } else {
      this.removeAllListeners();
    }
  }

  /**
   * 处理地图图层变化
   * @description 处理地图图层变化
   */
  handleMapChange() {
    console.log("handleMapChange");

    if (!this.isAutoUpdate) return;
    const layers = this.getMap().getLayers().getArray();
    if (!layers || layers.length === 0) return;
    this.setTreeDataBase(layers, true);
  }

  /**
   * 设置图层树形结构
   * @param {Array<LayerNode>} treeData 图层树形结构
   * @returns {void}
   */
  setTreeData(treeData) {
    if (!treeData) return;
    this.setTreeDataBase(treeData, false);
  }

  /**
   *  手动设置图层树形结构
   * @param {Array<LayerNode>} treeData 图层树形结构
   * @param {boolean} isTrack 是否跟踪图层变化
   */
  setTreeDataBase(treeData, isTrack) {
    if (!isTrack) {
      this.isAutoUpdate = false;
    }

    this.treeData_ = treeData;
    this.layerTree = [];
    this.layerList = [];

    const stack = [
      {
        source: treeData,
        target: this.layerTree,
      },
    ];

    while (stack.length) {
      const { source, target } = stack.pop();
      if (Array.isArray(source)) {
        source.forEach(node => {
          stack.push({
            source: node,
            target,
          });
        });
      } else {
        const id = source.id || (source.get && source.get("id"));
        const ignore =
          source.ignore ?? (source.get && source.get("ignore")) ?? false;

        if ((!(source instanceof BaseLayer) && !id) || ignore) continue;
        const node = new LayerNode(source, target);

        if (node.group && source instanceof LayerGroup) {
          this.listenLayerGroupChange(source);
        }
        this.layerList.push(node);
        if (node.group) {
          stack.push({
            source: source.children || source.getLayers().getArray(),
            target: node,
          });
        }
      }
    }

    this.updateElement();
  }

  /**
   * 更新元素
   * @description 更新元素
   */
  updateElement() {
    const defaultCheckedKeys = this.layerList
      .filter(node => node.visible)
      .map(node => node.id);

    const controlThis = this;

    createApp({
      data: () => {
        return {
          treeData: controlThis.layerTree,
          defaultProps: {
            label: "name",
            children: "children",
          },
          defaultCheckedKeys,
          folded: false,

          positionStyle: {
            left: "auto",
            top: "10px",
            right: "10px",
            bottom: "auto",
          },
          // 拖拽
          isDragging: false,
          offsetX: 0,
          offsetY: 0,
        };
      },
      watch: {
        positionStyle: {
          handler(newVal) {
            controlThis.element_.style.left = newVal.left;
            controlThis.element_.style.top = newVal.top;
            controlThis.element_.style.right = newVal.right;
            controlThis.element_.style.bottom = newVal.bottom;
          },
          deep: true,
          immediate: true,
        },
      },
      mounted() {
        controlThis.element_
          .querySelector(".ol-layer-tree-header")
          .addEventListener("mousedown", this.handleDraggableMouseDown);
        // 移除鼠标移动事件
        window.addEventListener("mousemove", this.handleDraggableMouseMove);

        // 移除鼠标松开事件
        window.addEventListener("mouseup", this.handleDraggableMouseUp);
      },
      unmounted() {
        controlThis.element_
          .querySelector(".ol-layer-tree-header")
          .removeEventListener("mousedown", this.handleDraggableMouseDown);
        // 移除鼠标移动事件
        window.removeEventListener("mousemove", this.handleDraggableMouseMove);

        // 移除鼠标松开事件
        window.removeEventListener("mouseup", this.handleDraggableMouseUp);
      },
      methods: {
        handleDraggableMouseDown(e) {
          this.isDragging = true;
          // 计算鼠标相对于面板左上角的偏移量
          this.offsetX = e.clientX - controlThis.element_.offsetLeft;
          this.offsetY = e.clientY - controlThis.element_.offsetTop;
        },

        handleDraggableMouseMove(e) {
          if (this.isDragging) {
            // 根据鼠标移动的偏移量更新面板的位置
            this.positionStyle.left = e.clientX - this.offsetX + "px";
            this.positionStyle.top = e.clientY - this.offsetY + "px";
            this.positionStyle.right = "auto";
            this.positionStyle.bottom = "auto";
          }
        },
        handleDraggableMouseUp() {
          this.isDragging = false;
        },

        /**
         * 图层节点勾选监听
         * @param {Object} node 图层节点数据
         * @param {boolean} checked 是否勾选
         * @param {boolean} childChecked 子图层是否勾选
         * @returns
         */
        handleNodeCheckChange(node, checked, childChecked) {
          node.visible = checked;
          if (!node.group) {
            const layer = getLayerById(map, node.id);
            if (!layer) return;
            layer.setVisible(checked);
          }
        },

        /**
         * 图层节点双击,定位到图层
         * @param {LayerNode} node 图层节点数据
         */
        handleNodeDoubleClick(node) {
          if (!node.visible) return;
          const layer = getLayerById(map, node.id);
          if (layer) {
            controlThis.flyToLayer(layer);
          }
        },

        /**
         * 图层节点透明度变化监听
         * @param {Event} e 事件对象
         * @param {LayerNode} node 图层节点数据
         */
        handleNodeOpacityChange(e, node) {
          const layer = getLayerById(map, node.id);
          if (layer) {
            layer.setOpacity(node.opacity);
          }
        },
        toggleFolded() {
          this.folded = !this.folded;
        },
      },
      template: `
       <div class="ol-layer-tree-container">
        <div class="ol-layer-tree-header" style="display: flex; align-items: center; justify-content: space-between;">
          <div class="ol-header-title">图层</div>
          <div class="ol-header-toggle" @click="toggleFolded" style="cursor:pointer;user-select:none;">
            <span v-if="!folded">&#9650;</span>
            <span v-else>&#9660;</span>
          </div>
        </div>
        <div class="ol-layer-tree-content" v-show="!folded">
          <el-tree
            :data="treeData"
            :props="defaultProps"
            :highlight-current="true"
            :expand-on-click-node="false"
            :default-checked-keys="defaultCheckedKeys"
            node-key="id"
            show-checkbox
            default-expand-all
            @check-change="handleNodeCheckChange"
            @check="handleNodeCheck"
          >
            <template #default="{ node, data }">
              <div class="custom-tree-node" @dblclick="handleNodeDoubleClick(data)">
                <span>{{ node.label }}</span>
                <el-slider
                  v-if="data.visible && !data.group"
                  v-model="data.opacity"
                  size="small"
                  :min="0"
                  :max="1"
                  :step="0.1"
                  @change="handleNodeOpacityChange($event, data)"
                  @click.native.stop />
              </div>
            </template>
          </el-tree>
          
        </div>
        <div class="ol-layer-tree-footer">
          <div class="tips">提示：双击可定位视域至其所在位置</div>
        </div>
       </div>
      `,
    })
      .use(ElementPlus)
      .mount(this.element_);
  }

  /**
   * 监听图层组变化
   * @param {LayerGroup} layerGroup 图层组
   */
  listenLayerGroupChange(layerGroup) {

    if (
      this.listenKeys.findIndex(
        item =>
          item.target instanceof LayerGroup && item.target.id === layerGroup.id
      ) !== -1
    )
      return;

    this.listenKeys.push(
      listen(
        layerGroup.getLayers(),
        "change:length",
        this.handleMapChange.bind(this)
      ),
      listen(layerGroup, "change:layers", this.handleMapChange.bind(this))
    );
  }

  /**
   * 移除所有事件监听
   * @description 移除所有事件监听
   */
  removeAllListeners() {
    this.listenKeys.forEach(key => {
      unlistenByKey(key);
    });
    this.listenKeys = [];
  }

  /**
   * 定位到图层
   * @param {BaseLayer} layer 图层
   */
  flyToLayer(layer) {
    let extent;

    if (layer instanceof LayerGroup) return;

    extent = layer.getExtent();

    if (!extent) {
      const source = layer.getSource();
      if (source instanceof ImageStatic) {
        extent = source.getImageExtent();
      } else if (source instanceof VectorSource) {
        extent = source.getExtent();
      } else if (source instanceof WMTSource) {
        extent = source.getTileGrid().getExtent();
      }
    }

    if (extent) {
      this.getMap()
        .getView()
        .fit(extent, {
          padding: [100, 100, 100, 100],
          duration: 1000,
        });
    }
  }
}

export default LayersManager;
